package com.fy.common.aspect;

import cn.hutool.core.util.StrUtil;
import com.fy.common.constants.Constants;
import com.fy.common.constants.ResultConstant;
import com.fy.ex.ConditionException;
import com.fy.common.annotation.GlobalInterceptor;
import com.fy.common.annotation.VerifyParam;
import com.fy.pojo.vo.user.LoginUserVO;
import com.fy.utils.VerifyUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Objects;

@Component("operationAspect")
@Aspect
@Slf4j
public class GlobalOperationAspect {

    private static Logger logger = LoggerFactory.getLogger(GlobalOperationAspect.class);
    private static final String TYPE_STRING = "java.lang.String";
    private static final String TYPE_INTEGER = "java.lang.Integer";
    private static final String TYPE_LONG = "java.lang.Long";

    @Pointcut("@annotation(com.fy.common.annotation.GlobalInterceptor)")
    private void requestInterceptor() {
    }

    @Before("requestInterceptor()")
    // 定义一个名为interceptorDo的方法，该方法接受一个JoinPoint类型的参数point，
    public void interceptorDo(JoinPoint point) throws ConditionException {
        try {
            // 获取被代理的目标对象，这个对象是当前正在被拦截的方法所属的对象
            Object target = point.getTarget();
            //log.info("目标对象的信息:{}", target);
            // 获取被拦截方法的参数数组，包含了调用该方法时传入的所有参数
            Object[] arguments = point.getArgs();
           /// log.info("参数数组:{}", arguments);
            // 获取被拦截方法的名称
            String methodName = point.getSignature().getName();
           // log.info("方法名称:{}", methodName);
            // 获取被拦截方法的参数类型数组，用于后续获取具体的方法对象
            Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
            // 根据目标对象的类、方法名称和参数类型获取对应的方法对象
            Method method = target.getClass().getMethod(methodName, parameterTypes);
            //log.info("方法对象信息:{}", method);
            // 获取被拦截方法上的GlobalInterceptor注解对象，如果方法上没有该注解，则返回null
            GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class);
            // 记录获取到的注解对象信息
            //log.info("inte:{}", interceptor);
            // 如果获取到的GlobalInterceptor注解对象为null，说明该方法不需要进行拦截相关的操作，直接返回
            if (null == interceptor) {
                return;
            }
            /**
             * 校验登录
             */
            if (interceptor.checkLogin() || interceptor.checkAdmin()) {
                checkLogin(interceptor.checkAdmin());
            }
            /**
             * 校验参数
             */
            if (interceptor.checkParams()) {
                validateParams(method, arguments);
            }
        } catch (ConditionException e) {
            logger.error("全局拦截器异常", e);
            throw e;
        } catch (Exception e) {
            logger.error("全局拦截器异常", e);
            throw new ConditionException(ResultConstant.LOGIN_ERROR);
        } catch (Throwable e) {
            logger.error("全局拦截器异常", e);
            throw new ConditionException(ResultConstant.LOGIN_ERROR);
        }
    }


    private void checkLogin(Boolean checkAdmin) {
        // 获取当前的HttpServletRequest对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 获取当前的HttpSession对象
        HttpSession session = request.getSession();
        // 从session中获取登录用户的信息
        LoginUserVO loginUserVO = (LoginUserVO) session.getAttribute(Constants.SESSION_KEY);

        // 如果session中没有登录用户的信息，抛出业务异常
        if (Objects.equals(loginUserVO, null)) {
            throw new ConditionException(ResultConstant.BUSINESS_ERROR);
        }

        // 如果需要检查管理员权限，并且当前用户不是管理员，抛出业务异常
        if (checkAdmin && !loginUserVO.getAdmin()) {
            throw new ConditionException(ResultConstant.NOT_ADMIN);
        }
    }


    private void validateParams(Method m, Object[] arguments) throws ConditionException {
        Parameter[] parameters = m.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object value = arguments[i];
            VerifyParam verifyParam = parameter.getAnnotation(VerifyParam.class);
            if (verifyParam == null) {
                continue;
            }
            //基本数据类型
            if (TYPE_STRING.equals(parameter.getParameterizedType().getTypeName()) || TYPE_LONG.equals(parameter.getParameterizedType().getTypeName()) || TYPE_INTEGER.equals(parameter.getParameterizedType().getTypeName())) {
                checkValue(value, verifyParam);
                //如果传递的是对象
            } else {
                checkObjValue(parameter, value);
            }
        }
    }

    private void checkObjValue(Parameter parameter, Object value) {
        try {
            String typeName = parameter.getParameterizedType().getTypeName();
            Class classz = Class.forName(typeName);
            Field[] fields = classz.getDeclaredFields();
            for (Field field : fields) {
                VerifyParam fieldVerifyParam = field.getAnnotation(VerifyParam.class);
                if (fieldVerifyParam == null) {
                    continue;
                }
                field.setAccessible(true);
                Object resultValue = field.get(value);
                checkValue(resultValue, fieldVerifyParam);
            }
        } catch (ConditionException e) {
            logger.error("校验参数错误", e);
            throw e;
        } catch (Exception e) {
            logger.error("校验参数错误", e);
            throw new ConditionException(ResultConstant.PARA_ERROR);
        }
    }

    /**
     * 校验参数
     *
     * @param value
     * @param verifyParam
     * @throws ConditionException
     */
    private void checkValue(Object value, VerifyParam verifyParam) throws ConditionException {
        Boolean isEmpty = value == null || StrUtil.isEmpty(value.toString());
        Integer length = value == null ? 0 : value.toString().length();

        /**
         * 校验空
         */
        if (isEmpty && verifyParam.required()) {
            throw new ConditionException("参数不能为空");
        }

        /**
         * 校验长度
         */
        if (!isEmpty && (verifyParam.max() != -1 && verifyParam.max() < length || verifyParam.min() != -1 && verifyParam.min() > length)) {
            throw new ConditionException("参数长度错误");
        }
        /**
         * 校验正则
         */
        if (!isEmpty && !StrUtil.isEmpty(verifyParam.regex().getRegex()) && !VerifyUtils.verify(String.valueOf(verifyParam.regex()), String.valueOf(value))) {
            throw new ConditionException(ResultConstant.PARA_ERROR);
        }
    }
}